From 56ff140a9cd3b073cabff65eb114b452fa65cfa3 Mon Sep 17 00:00:00 2001 From: Kohei Watanabe <31376689+nabekou29@users.noreply.github.com> Date: Sun, 8 Sep 2024 03:25:51 +0900 Subject: [PATCH] feat: Smarter default language detection (#26) --- README-ja.md | 5 +- README.md | 5 +- .../en/translation.json => messages/en.json} | 0 .../ja/translation.json => messages/ja.json} | 0 lua/js-i18n/config.lua | 33 ++- lua/js-i18n/lang_name_list.lua | 236 ++++++++++++++++++ tests/js-i18n/config_spec.lua | 33 +++ .../en/translation.json => messages/en.json} | 0 .../ja/translation.json => messages/ja.json} | 0 9 files changed, 299 insertions(+), 13 deletions(-) rename demo/projects/next-intl/{locales/en/translation.json => messages/en.json} (100%) rename demo/projects/next-intl/{locales/ja/translation.json => messages/ja.json} (100%) create mode 100644 lua/js-i18n/lang_name_list.lua create mode 100644 tests/js-i18n/config_spec.lua rename tests/projects/next-intl/{locales/en/translation.json => messages/en.json} (100%) rename tests/projects/next-intl/{locales/ja/translation.json => messages/ja.json} (100%) diff --git a/README-ja.md b/README-ja.md index 1ac4404..9c90dd1 100644 --- a/README-ja.md +++ b/README-ja.md @@ -91,8 +91,8 @@ js-i18n.nvim は、JavaScript の i18n ライブラリをサポートする Neov ```lua { primary_language = {}, -- 優先表示する言語(バーチャルテキストなどの表示に使用する言語の初期設定) - translation_source = { "**/locales/*/translation.json" }, -- 翻訳リソースのパターン - detect_language = ..., -- 言語を検出する関数。 + translation_source = { "**/{locales,messages}/*.json" }, -- 翻訳リソースのパターン + detect_language = ..., -- 言語を検出する関数。デフォルトではファイル名からヒューリスティックに検出する関数が使用されます。 key_separator = ".", -- キーのセパレータ virt_text = { enabled = true, -- バーチャルテキストの表示を有効にする @@ -113,4 +113,3 @@ js-i18n.nvim は、JavaScript の i18n ライブラリをサポートする Neov - ライブラリサポートの強化 - namespace のサポート -- 今より賢い翻訳リソースの検出 diff --git a/README.md b/README.md index 9d6c8f2..30b5889 100644 --- a/README.md +++ b/README.md @@ -96,8 +96,8 @@ The default settings are as follows. For omitted parts, refer to [config.lua](./ ```lua { primary_language = {}, -- The default language to display (initial setting for displaying virtual text, etc.) - translation_source = { "**/locales/*/translation.json" }, -- Pattern for translation resources - detect_language = ..., -- Function to detect the language. + translation_source = { "**/{locales,messages}/*.json" }, -- Pattern for translation resources + detect_language = ..., -- Function to detect the language. By default, a function that detects the language heuristically from the file name is used. key_separator = ".", -- Key separator virt_text = { enabled = true, -- Enable virtual text display @@ -118,4 +118,3 @@ The default settings are as follows. For omitted parts, refer to [config.lua](./ - Enhanced support for libraries - Namespace support -- Smarter translation resource detection diff --git a/demo/projects/next-intl/locales/en/translation.json b/demo/projects/next-intl/messages/en.json similarity index 100% rename from demo/projects/next-intl/locales/en/translation.json rename to demo/projects/next-intl/messages/en.json diff --git a/demo/projects/next-intl/locales/ja/translation.json b/demo/projects/next-intl/messages/ja.json similarity index 100% rename from demo/projects/next-intl/locales/ja/translation.json rename to demo/projects/next-intl/messages/ja.json diff --git a/lua/js-i18n/config.lua b/lua/js-i18n/config.lua index eba205b..143ef1b 100644 --- a/lua/js-i18n/config.lua +++ b/lua/js-i18n/config.lua @@ -1,13 +1,32 @@ +local langs = require("js-i18n.lang_name_list") local utils = require("js-i18n.utils") local M = {} ---- ファイルパスから言語を検出する ---- @param path string ファイルパス -local function default_detect_language(path) +local function normalize_lang(locale) + return locale:lower():gsub("-", "_") +end + +local LangSet = {} +for _, l in ipairs(langs) do + LangSet[normalize_lang(l)] = true +end + +--- Detect language from file path heuristically +--- @param path string File path +function M.default_detect_language(path) local abs_path = vim.fn.fnamemodify(path, ":p") - local split = vim.split(abs_path, "/") - local lang = split[#split - 1] + local split = vim.split(abs_path, "[/.]") + + local lang = nil + + for _, part in ipairs(vim.fn.reverse(split)) do + if LangSet[normalize_lang(part)] then + lang = part + break + end + end + return lang end @@ -56,8 +75,8 @@ end --- @type I18n.Config local default_config = { primary_language = {}, - translation_source = { "**/locales/**/*.json" }, - detect_language = default_detect_language, + translation_source = { "**/{locales,messages}/*.json" }, + detect_language = M.default_detect_language, key_separator = ".", virt_text = { enabled = true, diff --git a/lua/js-i18n/lang_name_list.lua b/lua/js-i18n/lang_name_list.lua new file mode 100644 index 0000000..8af3563 --- /dev/null +++ b/lua/js-i18n/lang_name_list.lua @@ -0,0 +1,236 @@ +-- http://tools.ietf.org/html/rfc5646 + +return { + "af", + "af-ZA", + "ar", + "ar-AE", + "ar-BH", + "ar-DZ", + "ar-EG", + "ar-IQ", + "ar-JO", + "ar-KW", + "ar-LB", + "ar-LY", + "ar-MA", + "ar-OM", + "ar-QA", + "ar-SA", + "ar-SY", + "ar-TN", + "ar-YE", + "az", + "az-AZ", + "az-Cyrl-AZ", + "be", + "be-BY", + "bg", + "bg-BG", + "bs-BA", + "ca", + "ca-ES", + "cs", + "cs-CZ", + "cy", + "cy-GB", + "da", + "da-DK", + "de", + "de-AT", + "de-CH", + "de-DE", + "de-LI", + "de-LU", + "dv", + "dv-MV", + "el", + "el-GR", + "en", + "en-AU", + "en-BZ", + "en-CA", + "en-CB", + "en-GB", + "en-IE", + "en-JM", + "en-NZ", + "en-PH", + "en-TT", + "en-US", + "en-ZA", + "en-ZW", + "eo", + "es", + "es-AR", + "es-BO", + "es-CL", + "es-CO", + "es-CR", + "es-DO", + "es-EC", + "es-ES", + "es-GT", + "es-HN", + "es-MX", + "es-NI", + "es-PA", + "es-PE", + "es-PR", + "es-PY", + "es-SV", + "es-UY", + "es-VE", + "et", + "et-EE", + "eu", + "eu-ES", + "fa", + "fa-IR", + "fi", + "fi-FI", + "fo", + "fo-FO", + "fr", + "fr-BE", + "fr-CA", + "fr-CH", + "fr-FR", + "fr-LU", + "fr-MC", + "gl", + "gl-ES", + "gu", + "gu-IN", + "he", + "he-IL", + "hi", + "hi-IN", + "hr", + "hr-BA", + "hr-HR", + "hu", + "hu-HU", + "hy", + "hy-AM", + "id", + "id-ID", + "is", + "is-IS", + "it", + "it-CH", + "it-IT", + "ja", + "ja-JP", + "ka", + "ka-GE", + "kk", + "kk-KZ", + "kn", + "kn-IN", + "ko", + "ko-KR", + "kok", + "kok-IN", + "ky", + "ky-KG", + "lt", + "lt-LT", + "lv", + "lv-LV", + "mi", + "mi-NZ", + "mk", + "mk-MK", + "mn", + "mn-MN", + "mr", + "mr-IN", + "ms", + "ms-BN", + "ms-MY", + "mt", + "mt-MT", + "nb", + "nb-NO", + "nl", + "nl-BE", + "nl-NL", + "nn-NO", + "ns", + "ns-ZA", + "pa", + "pa-IN", + "pl", + "pl-PL", + "ps", + "ps-AR", + "pt", + "pt-BR", + "pt-PT", + "qu", + "qu-BO", + "qu-EC", + "qu-PE", + "ro", + "ro-RO", + "ru", + "ru-RU", + "sa", + "sa-IN", + "se", + "se-FI", + "se-NO", + "se-SE", + "sk", + "sk-SK", + "sl", + "sl-SI", + "sq", + "sq-AL", + "sr-BA", + "sr-Cyrl-BA", + "sr-SP", + "sr-Cyrl-SP", + "sv", + "sv-FI", + "sv-SE", + "sw", + "sw-KE", + "syr", + "syr-SY", + "ta", + "ta-IN", + "te", + "te-IN", + "th", + "th-TH", + "tl", + "tl-PH", + "tn", + "tn-ZA", + "tr", + "tr-TR", + "tt", + "tt-RU", + "ts", + "uk", + "uk-UA", + "ur", + "ur-PK", + "uz", + "uz-UZ", + "uz-Cyrl-UZ", + "vi", + "vi-VN", + "xh", + "xh-ZA", + "zh", + "zh-CN", + "zh-HK", + "zh-MO", + "zh-SG", + "zh-TW", + "zu", + "zu-ZA", +} diff --git a/tests/js-i18n/config_spec.lua b/tests/js-i18n/config_spec.lua new file mode 100644 index 0000000..a444d86 --- /dev/null +++ b/tests/js-i18n/config_spec.lua @@ -0,0 +1,33 @@ +local config = require("js-i18n.config") + +describe("js-i18n.config", function() + describe("default_detect_language", function() + local tests = { + { path = "/path/to/locals/en/trans.json", expected = "en" }, + { path = "/path/to/locals/ja/trans.json", expected = "ja" }, + { path = "/path/to/locals/hoge/trans.json", expected = nil }, + + -- Test cases to verify that it is sufficient for the languagee name to be included somewhere. + { path = "/path/to/locals/sub/en.json", expected = "en" }, + { path = "/path/to/en/locals/trans.json", expected = "en" }, + { path = "/path/to/locals/en-trans.json", expected = nil }, + + -- Test cases for language names with any case and separating characters. + { path = "/path/to/locals/en-us/trans.json", expected = "en-us" }, + { path = "/path/to/locals/en_us/trans.json", expected = "en_us" }, + { path = "/path/to/locals/en-US/trans.json", expected = "en-US" }, + + -- Test cases where the last match is returned when multiple locale names are included. + { path = "/path/to/locals/en/ja.json", expected = "ja" }, + } + + for _, test in ipairs(tests) do + it( + string.format("should return %q when detecting language from %q", test.expected, test.path), + function() + assert.are.equal(test.expected, config.default_detect_language(test.path)) + end + ) + end + end) +end) diff --git a/tests/projects/next-intl/locales/en/translation.json b/tests/projects/next-intl/messages/en.json similarity index 100% rename from tests/projects/next-intl/locales/en/translation.json rename to tests/projects/next-intl/messages/en.json diff --git a/tests/projects/next-intl/locales/ja/translation.json b/tests/projects/next-intl/messages/ja.json similarity index 100% rename from tests/projects/next-intl/locales/ja/translation.json rename to tests/projects/next-intl/messages/ja.json