From a24e4fa0b990214b369b59704193eb64be27483c Mon Sep 17 00:00:00 2001 From: Konstantin Date: Wed, 4 May 2016 13:24:34 +0300 Subject: First working version. --- .gitignore | 73 ++++++++++++++ CMakeLists.txt | 6 ++ categories.c | 119 ++++++++++++++++++++++ categories.h | 32 ++++++ cmake/TranslationsMusl.cmake | 118 ++++++++++++++++++++++ locale.c | 233 +++++++++++++++++++++++++++++++++++++++++++ po/CMakeLists.txt | 4 + 7 files changed, 585 insertions(+) create mode 100644 .gitignore create mode 100644 CMakeLists.txt create mode 100644 categories.c create mode 100644 categories.h create mode 100644 cmake/TranslationsMusl.cmake create mode 100644 locale.c create mode 100644 po/CMakeLists.txt diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..fab7372 --- /dev/null +++ b/.gitignore @@ -0,0 +1,73 @@ +# This file is used to ignore files which are generated +# ---------------------------------------------------------------------------- + +*~ +*.autosave +*.a +*.core +*.moc +*.o +*.obj +*.orig +*.rej +*.so +*.so.* +*_pch.h.cpp +*_resource.rc +*.qm +.#* +*.*# +core +!core/ +tags +.DS_Store +.directory +*.debug +Makefile* +*.prl +*.app +moc_*.cpp +ui_*.h +qrc_*.cpp +Thumbs.db +*.res +*.rc +/.qmake.cache +/.qmake.stash + +# qtcreator generated files +*.pro.user* + +# xemacs temporary files +*.flc + +# Vim temporary files +.*.swp + +# Visual Studio generated files +*.ib_pdb_index +*.idb +*.ilk +*.pdb +*.sln +*.suo +*.vcproj +*vcproj.*.*.user +*.ncb +*.sdf +*.opensdf +*.vcxproj +*vcxproj.* + +# MinGW generated files +*.Debug +*.Release + +# Python byte code +*.pyc + +# Binaries +# -------- +*.dll +*.exe + diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..7483948 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,6 @@ +project(locales C) +cmake_minimum_required(VERSION 2.8) +list (APPEND CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake) +set(LOCALE_SOURCES locale.c categories.c categories.h) +add_executable(locale ${LOCALE_SOURCES}) +add_subdirectory(po) diff --git a/categories.c b/categories.c new file mode 100644 index 0000000..8d8ae9e --- /dev/null +++ b/categories.c @@ -0,0 +1,119 @@ +#include "categories.h" +#include +#include + +struct cat_item lc_time_cats [] = +{ + {ABDAY_1,"abday",CAT_TYPE_STRINGARRAY,ABDAY_1,ABDAY_7}, + {DAY_1,"day",CAT_TYPE_STRINGARRAY,DAY_1,DAY_7}, + {ABMON_1,"abmon",CAT_TYPE_STRINGARRAY,ABMON_1,ABMON_12}, + {MON_1,"mon",CAT_TYPE_STRINGARRAY,MON_1,MON_12}, + {AM_STR,"am_pm", CAT_TYPE_STRINGARRAY,AM_STR,PM_STR}, + {D_T_FMT,"d_t_fmt", CAT_TYPE_STRING,0,0}, + {D_FMT,"d_fmt", CAT_TYPE_STRING,0,0}, + {T_FMT,"t_fmt", CAT_TYPE_STRING,0,0}, + {ERA,"era", CAT_TYPE_STRING,0,0}, + {ERA_D_FMT,"era_d_fmt", CAT_TYPE_STRING,0,0}, + {ALT_DIGITS,"alt_digits", CAT_TYPE_STRING,0,0}, + {ERA_D_T_FMT,"era_d_t_fmt", CAT_TYPE_STRING,0,0}, + {ERA_T_FMT,"era_t_fmt", CAT_TYPE_STRING,0,0}, + {0,"", CAT_TYPE_END,0,0} +}; + +struct cat_item lc_ctype_cats [] = +{ + {CODESET, "charmap", CAT_TYPE_STRING,0,0}, + {0,"", CAT_TYPE_END,0,0} +}; + +struct cat_item lc_messages_cats [] = +{ + {YESEXPR, "yesexpr",CAT_TYPE_STRING,0,0}, + {NOEXPR, "noexpr",CAT_TYPE_STRING,0,0}, +#if defined(_GNU_SOURCE) || defined(_BSD_SOURCE) + {YESSTR, "yesstr",CAT_TYPE_STRING,0,0}, + {NOSTR, "nostr",CAT_TYPE_STRING,0,0}, +#endif + {0,"", CAT_TYPE_END,0,0} +}; + +struct cat_item lc_numeric_cats [] = +{ + {RADIXCHAR, "decimal_point",CAT_TYPE_STRING,0,0}, + {THOUSEP, "thousands_sep",CAT_TYPE_STRING,0,0}, + {0,"", CAT_TYPE_END,0,0} +}; + +struct cat_item lc_monetary_cats [] = +{ + {CRNCYSTR, "crncystr",CAT_TYPE_STRING,0,0}, + {0,"", CAT_TYPE_END,0,0} +}; + +struct cat_item lc_collate_cats [] = +{ + {0,"", CAT_TYPE_END,0,0} +}; + +struct cat_item* cats[] = +{ + lc_ctype_cats, + lc_numeric_cats, + lc_time_cats, + lc_collate_cats, + lc_monetary_cats, + lc_messages_cats +}; + +const struct cat_item get_cat_item_for_name(const char *name) +{ + struct cat_item invalid = {0,"", CAT_TYPE_END,0,0}; + for(int i = 0; i < LC_ALL; i++) + { + for(int j = 0; cats[i][j].type != CAT_TYPE_END; j++) + { + if(!strcmp(name,cats[i][j].name)) + return cats[i][j]; + } + } + return invalid; +} + +const struct cat_item* get_cat_for_locale_cat(int locale_cat) +{ + return cats[locale_cat]; +} + +int get_cat_num_for_name(const char* cat_name) +{ + if (!strcmp(cat_name,"LC_CTYPE")) + return LC_CTYPE; + if (!strcmp(cat_name,"LC_NUMERIC")) + return LC_NUMERIC; + if (!strcmp(cat_name,"LC_TIME")) + return LC_TIME; + if (!strcmp(cat_name,"LC_COLLATE")) + return LC_COLLATE; + if (!strcmp(cat_name,"LC_MONETARY")) + return LC_MONETARY; + if (!strcmp(cat_name,"LC_MESSAGES")) + return LC_MESSAGES; + return LC_INVAL; +} + +const char *get_name_for_cat(int cat_no) +{ + if (cat_no == LC_CTYPE) + return "LC_CTYPE"; + if (cat_no == LC_NUMERIC) + return "LC_NUMERIC"; + if (cat_no == LC_TIME) + return "LC_TIME"; + if (cat_no == LC_COLLATE) + return "LC_COLLATE"; + if (cat_no == LC_MONETARY) + return "LC_MONETARY"; + if (cat_no == LC_MESSAGES) + return "LC_MESSAGES"; + return ""; +} diff --git a/categories.h b/categories.h new file mode 100644 index 0000000..9a5bec0 --- /dev/null +++ b/categories.h @@ -0,0 +1,32 @@ +#ifndef CATEGORIES_H +#define CATEGORIES_H + +#include +#include + +#define CAT_TYPE_STRING 0 +#define CAT_TYPE_STRINGARRAY 1 +#define CAT_TYPE_STRINGLIST 2 +#define CAT_TYPE_BYTE 3 +#define CAT_TYPE_BYTEARRAY 4 +#define CAT_TYPE_WORD 4 +#define CAT_TYPE_WORDARRAY 5 +#define CAT_TYPE_END 6 + +#define LC_INVAL -1 + +typedef int cat_type; + +struct cat_item +{ + nl_item id; + const char* name; + cat_type type; + int min; + int max; +}; +const struct cat_item get_cat_item_for_name(const char* name); +const struct cat_item* get_cat_for_locale_cat(int locale_cat); +int get_cat_num_for_name(const char *cat_name); +const char* get_name_for_cat(int cat_no); +#endif // CATEGORIES_H diff --git a/cmake/TranslationsMusl.cmake b/cmake/TranslationsMusl.cmake new file mode 100644 index 0000000..d944b70 --- /dev/null +++ b/cmake/TranslationsMusl.cmake @@ -0,0 +1,118 @@ +# Translations.cmake, CMake macros written for Marlin, feel free to re-use them + +macro (add_translations_directory NLS_PACKAGE LOCPATH) + add_custom_target (i18n ALL COMMENT ?Building i18n messages.?) + find_program (MSGFMT_EXECUTABLE msgfmt) + # be sure that all languages are present + # Using all usual languages code from https://www.gnu.org/software/gettext/manual/html_node/Language-Codes.html#Language-Codes + # Rare language codes should be added on-demand. + file(STRINGS LOCALES LANGUAGES_NEEDED) +# set (LANGUAGES_NEEDED aa ab ae af ak am an ar as ast av ay az ba be bg bh bi bm bn bo br bs ca ce ch ckb co cr cs cu cv cy da de dv dz ee el en_AU en_CA en_GB eo es et eu fa ff fi fj fo fr fr_CA fy ga gd gl gn gu gv ha he hi ho hr ht hu hy hz ia id ie ig ii ik io is it iu ja jv ka kg ki kj kk kl km kn ko kr ks ku kv kw ky la lb lg li ln lo lt lu lv mg mh mi mk ml mn mo mr ms mt my na nb nd ne ng nl nn nb nr nv ny oc oj om or os pa pi pl ps pt pt_BR qu rm rn ro ru rue rw sa sc sd se sg si sk sl sm sma sn so sq sr ss st su sv sw ta te tg th ti tk tl tn to tr ts tt tw ty ug uk ur uz ve vi vo wa wo xh yi yo za zh zh_CN zh_HK zh_TW zu) +# string (REPLACE ";" " " LINGUAS "${LANGUAGES_NEEDED}") +# configure_file (${CMAKE_CURRENT_SOURCE_DIR}/LINGUAS.in ${CMAKE_CURRENT_BINARY_DIR}/LINGUAS) + foreach (LANGUAGE_NEEDED ${LANGUAGES_NEEDED}) + create_po_file (${LANGUAGE_NEEDED}) + endforeach (LANGUAGE_NEEDED ${LANGUAGES_NEEDED}) + # generate .mo from .po + file (GLOB PO_FILES ${CMAKE_CURRENT_SOURCE_DIR}/*.po) + foreach (PO_INPUT ${PO_FILES}) + get_filename_component (PO_INPUT_BASE ${PO_INPUT} NAME_WE) + set (MO_OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/${PO_INPUT_BASE}.mo) + set (PO_COPY ${CMAKE_CURRENT_BINARY_DIR}/${PO_INPUT_BASE}.po) + file (COPY ${PO_INPUT} DESTINATION ${CMAKE_CURRENT_BINARY_DIR}) + add_custom_command (TARGET i18n COMMAND ${MSGFMT_EXECUTABLE} -o ${MO_OUTPUT} ${PO_INPUT}) + + install (FILES ${MO_OUTPUT} DESTINATION + ${LOCPATH}) + endforeach (PO_INPUT ${PO_FILES}) +endmacro (add_translations_directory) + +# Apply the right default template. +macro (create_po_file LANGUAGE_NEEDED) + set (FILE ${CMAKE_CURRENT_SOURCE_DIR}/${LANGUAGE_NEEDED}.po) + if (NOT EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/${LANGUAGE_NEEDED}.po) + file (APPEND ${FILE} "msgid \"\"\n") + file (APPEND ${FILE} "msgstr \"\"\n") + file (APPEND ${FILE} "\"MIME-Version: 1.0\\n\"\n") + file (APPEND ${FILE} "\"Content-Type: text/plain; charset=UTF-8\\n\"\n") + + if ("${LANGUAGE_NEEDED}" STREQUAL "ja" + OR "${LANGUAGE_NEEDED}" STREQUAL "vi" + OR "${LANGUAGE_NEEDED}" STREQUAL "ko") + file (APPEND ${FILE} "\"Plural-Forms: nplurals=2; plural=n == 1 ? 0 : 1;\\n\"\n") + elseif ("${LANGUAGE_NEEDED}" STREQUAL "en" + OR "${LANGUAGE_NEEDED}" STREQUAL "de" + OR "${LANGUAGE_NEEDED}" STREQUAL "nl" + OR "${LANGUAGE_NEEDED}" STREQUAL "sv" + OR "${LANGUAGE_NEEDED}" STREQUAL "nb" + OR "${LANGUAGE_NEEDED}" STREQUAL "nn" + OR "${LANGUAGE_NEEDED}" STREQUAL "nb" + OR "${LANGUAGE_NEEDED}" STREQUAL "no" + OR "${LANGUAGE_NEEDED}" STREQUAL "fo" + OR "${LANGUAGE_NEEDED}" STREQUAL "es" + OR "${LANGUAGE_NEEDED}" STREQUAL "pt" + OR "${LANGUAGE_NEEDED}" STREQUAL "it" + OR "${LANGUAGE_NEEDED}" STREQUAL "bg" + OR "${LANGUAGE_NEEDED}" STREQUAL "he" + OR "${LANGUAGE_NEEDED}" STREQUAL "fi" + OR "${LANGUAGE_NEEDED}" STREQUAL "et" + OR "${LANGUAGE_NEEDED}" STREQUAL "eo" + OR "${LANGUAGE_NEEDED}" STREQUAL "hu" + OR "${LANGUAGE_NEEDED}" STREQUAL "tr" + OR "${LANGUAGE_NEEDED}" STREQUAL "es") + file (APPEND ${FILE} "\"Plural-Forms: nplurals=2; plural=n != 1;\\n\"\n") + elseif ("${LANGUAGE_NEEDED}" STREQUAL "fr" + OR "${LANGUAGE_NEEDED}" STREQUAL "fr_CA" + OR "${LANGUAGE_NEEDED}" STREQUAL "pt_BR") + file (APPEND ${FILE} "\"Plural-Forms: nplurals=2; plural=n>1;\\n\"\n") + elseif ("${LANGUAGE_NEEDED}" STREQUAL "lv") + file (APPEND ${FILE} "\"Plural-Forms: nplurals=3; plural=n%10==1 && n%100!=11 ? 0 : n != 0 ? 1 : 2;\\n\"\n") + elseif ("${LANGUAGE_NEEDED}" STREQUAL "ro") + file (APPEND ${FILE} "\"Plural-Forms: nplurals=3; plural=n==1 ? 0 : (n==0 || (n%100 > 0 && n%100 < 20)) ? 1 : 2;\\n\"\n") + elseif ("${LANGUAGE_NEEDED}" STREQUAL "lt") + file (APPEND ${FILE} "\"Plural-Forms: nplurals=3; plural=n%10==1 && n%100!=11 ? 0 : n%10>=2 && (n%100<10 || n%100>=20) ? 1 : 2;\\n\"\n") + elseif ("${LANGUAGE_NEEDED}" STREQUAL "ru" + OR "${LANGUAGE_NEEDED}" STREQUAL "uk" + OR "${LANGUAGE_NEEDED}" STREQUAL "be" + OR "${LANGUAGE_NEEDED}" STREQUAL "sr" + OR "${LANGUAGE_NEEDED}" STREQUAL "hr") + file (APPEND ${FILE} "\"Plural-Forms: nplurals=3; plural=n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;\\n\"\n") + elseif ("${LANGUAGE_NEEDED}" STREQUAL "cs" + OR "${LANGUAGE_NEEDED}" STREQUAL "sk") + file (APPEND ${FILE} "\"Plural-Forms: nplurals=3; plural=(n==1) ? 0 : (n>=2 && n<=4) ? 1 : 2;\\n\"\n") + elseif ("${LANGUAGE_NEEDED}" STREQUAL "pl") + file (APPEND ${FILE} "\"Plural-Forms: nplurals=3; plural=n==1 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;\\n\"\n") + elseif ("${LANGUAGE_NEEDED}" STREQUAL "sl") + file (APPEND ${FILE} "\"Plural-Forms: nplurals=4; plural=n%100==1 ? 0 : n%100==2 ? 1 : n%100==3 || n%100==4 ? 2 : 3;\\n\"\n") + endif () + + endif () +endmacro (create_po_file) + +macro (add_translations_catalog NLS_PACKAGE) + add_custom_target (pot COMMENT ?Building translation catalog.?) + find_program (XGETTEXT_EXECUTABLE xgettext) + + set(C_SOURCE "") + + foreach(FILES_INPUT ${ARGN}) + set(BASE_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/${FILES_INPUT}) + + file (GLOB_RECURSE SOURCE_FILES RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}/ ${BASE_DIRECTORY}/*.c) + foreach(C_FILE ${SOURCE_FILES}) + set(C_SOURCE ${C_SOURCE} ${C_FILE}) + endforeach() + endforeach() + + set(BASE_XGETTEXT_COMMAND + ${XGETTEXT_EXECUTABLE} -d ${NLS_PACKAGE} + -o ${CMAKE_CURRENT_SOURCE_DIR}/${NLS_PACKAGE}.pot + --add-comments="/" --extract-all --from-code=UTF-8) + + set(CONTINUE_FLAG "") + + IF(NOT "${C_SOURCE}" STREQUAL "") + add_custom_command(TARGET pot WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} COMMAND ${BASE_XGETTEXT_COMMAND} ${C_SOURCE}) + set(CONTINUE_FLAG "-j") + ENDIF() +endmacro () diff --git a/locale.c b/locale.c new file mode 100644 index 0000000..6079076 --- /dev/null +++ b/locale.c @@ -0,0 +1,233 @@ +/* vi: set sw=4 ts=4: */ +/* + * + * Copyright (c) 2008 STMicroelectronics Ltd + * Filippo Arcidiacono (filippo.arcidiacono@st.com) + * + * Licensed under the LGPL v2.1, see the file COPYING.LIB in this tarball. + * + * A 'locale' command implementation for uClibc. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "categories.h" + + +/* If set print the name of the category. */ +static bool show_category_name = 0; + +/* If set print the name of the item. */ +static bool show_keyword_name = 0; + +/* If set print the usage command. */ +static bool show_usage = 0; + +/* Print names of all available locales. */ +static bool do_all = 0; + +/* Print names of all available character maps. */ +static bool do_charmaps = 0; + +static int remaining = 0; + +static void usage(char *name) +{ + char *s; + + s = basename(name); + fprintf(stderr, + "Usage: %s [-a | -m] [FORMAT] name...\n\n" + "\t-a, --all-locales\tWrite names of all available locales\n" + "\t-m, --charmaps\tWrite names of available charmaps\n" + "\nFORMAT:\n" + "\t-c, --category-name\tWrite names of selected categories\n" + "\t-k, --keyword-name\tWrite names of selected keywords\n" + , s); + free(s); +} + +static int argp_parse(int argc, char *argv[]) +{ + int c; + char *progname; + static const struct option long_options[] = { + {"all-locales", no_argument, NULL, 'a'}, + {"charmaps", no_argument, NULL, 'm'}, + {"category-name", no_argument, NULL, 'c'}, + {"keyword-name", no_argument, NULL, 'k'}, + {"help", no_argument, NULL, 'h'}, + {NULL, 0, NULL, 0}}; + progname = *argv; + while ((c = getopt_long(argc, argv, "amckh", long_options, NULL)) >= 0) + + switch (c) { + case 'a': + do_all = 1; + break; + case 'c': + show_category_name = 1; + break; + case 'm': + do_charmaps = 1; + break; + case 'k': + show_keyword_name = 1; + break; + case 'h': + show_usage = 1; + break; + case '?': + fprintf(stderr, "Unknown option.\n"); + usage(progname); + return 1; + + default: + fprintf(stderr, "This should never happen!\n"); + return 1; + } + + remaining = optind; + return 0; +} + +static void list_locale() +{ + const char *locpath = getenv("MUSL_LOCPATH"); + DIR *dir = opendir(locpath); + struct dirent *pDir; + printf("C"); + printf("C.UTF-8"); + while ((pDir = readdir(dir)) != NULL){ + printf("%s.%s\n",pDir->d_name,"UTF-8"); + } +} + +static void list_charmaps() +{ + printf("ASCII\n"); + printf("UTF-8\n"); + return; +} + +static void print_item (struct cat_item item) +{ + switch (item.type) + { + case CAT_TYPE_STRING: + if (show_keyword_name) + printf ("%s=\"", item.name); + fputs (nl_langinfo (item.id) ? : "", stdout); + if (show_keyword_name) + putchar ('"'); + putchar ('\n'); + break; + case CAT_TYPE_STRINGARRAY: + { + const char *val; + if (show_keyword_name) + printf ("%s=\"", item.name); + + for (int cnt = item.min; cnt <= item.max; ++cnt) + { + val = nl_langinfo (item.id); + if (val != NULL) + fputs (val, stdout); + putchar (';'); + } + if (show_keyword_name) + putchar ('"'); + putchar ('\n'); + } + break; + } +} + +/* Show the information request for NAME. */ +static void show_info(const char *name) +{ + for (size_t cat_no = 0; cat_no < LC_ALL; ++cat_no) + { + if (strcmp (name, get_name_for_cat(cat_no)) == 0) + /* Print the whole category. */ + { + if (show_category_name != 0) + puts (get_name_for_cat(cat_no)); + const struct cat_item* items = get_cat_for_locale_cat(cat_no); + for(int j = 0; items[j].type != CAT_TYPE_END; j++) + print_item(items[j]); + return; + } + else + { + if (show_category_name != 0) + puts (get_name_for_cat(cat_no)); + print_item(get_cat_item_for_name(name)); + } + + } +} + +static void show_locale_vars() +{ + const char *lcall = getenv("LC_ALL") ? : "\0"; + const char *lang = getenv("LANG") ? : ""; + + /* LANG has to be the first value. */ + printf("LANG=%s\n", lang); + for (size_t cat_no = 0; cat_no < LC_ALL; ++cat_no) + { + printf("%s=%s\n",get_name_for_cat(cat_no),lcall[0] != '\0' ? lcall + : lang[0] != '\0' ? lang + : "POSIX"); + } + /* The last is the LC_ALL value. */ + printf("LC_ALL=%s\n", lcall); +} + +int main(int argc, char *argv[]) +{ + /* Parse and process arguments. */ + if (argp_parse(argc, argv)) + return 1; + + if (do_all) { + list_locale(); + exit(EXIT_SUCCESS); + } + + if (do_charmaps) { + list_charmaps(); + exit(EXIT_SUCCESS); + } + + if (show_usage) { + usage(*argv); + exit(EXIT_SUCCESS); + } + + /* If no real argument is given we have to print the contents of the + current locale definition variables. These are LANG and the LC_*. */ + if (remaining == argc && show_category_name == 0 + && show_keyword_name == 0) { + show_locale_vars(); + exit(EXIT_SUCCESS); + } + + /* Process all given names. */ + while (remaining < argc) + show_info(argv[remaining++]); + + exit(EXIT_SUCCESS); +} diff --git a/po/CMakeLists.txt b/po/CMakeLists.txt new file mode 100644 index 0000000..606b49a --- /dev/null +++ b/po/CMakeLists.txt @@ -0,0 +1,4 @@ +include(TranslationsMusl) +add_translations_directory("musl" MUSL_LOCPATH) +add_translations_catalog("musl" + ../musl) -- cgit v1.2.3-70-g09d2