require 'sinatra' require 'json' require 'yaml' # Configure Sinatra set :port, ENV['PORT'] || 4567 set :bind, '0.0.0.0' # Load responses from YAML file RESPONSES = YAML.load_file('responses.yml') DEFAULT_LANGUAGE = 'en' # Helper method to get a random "no" response def get_no_response(lang = DEFAULT_LANGUAGE) language = RESPONSES[lang] || RESPONSES[DEFAULT_LANGUAGE] language['responses'].sample end # Helper method to get language name def get_language_name(lang) language = RESPONSES[lang] || RESPONSES[DEFAULT_LANGUAGE] language['name'] end # Helper method to get available languages def get_available_languages RESPONSES.map { |code, data| { code: code, name: data['name'] } } end # Helper method to determine language from request def determine_language(params, headers) # Priority: URL parameter > Accept-Language header > default return params[:lang] if params[:lang] && RESPONSES[params[:lang]] # Parse Accept-Language header if headers['HTTP_ACCEPT_LANGUAGE'] accepted_langs = headers['HTTP_ACCEPT_LANGUAGE'] .split(',') .map { |lang| lang.split(';').first.downcase.strip } # Check for exact matches first accepted_langs.each do |lang| return lang if RESPONSES[lang] end # Check for language family matches (e.g., 'nb' or 'nn' -> 'no') accepted_langs.each do |lang| case lang when /^nb|nn/ # Norwegian Bokmål/Nynorsk return 'no' when /^sv/ # Swedish variants return 'sv' when /^da/ # Danish variants return 'da' when /^is/ # Icelandic variants return 'is' when /^fi/ # Finnish variants return 'fi' when /^fo/ # Faroese variants return 'fo' when /^se|smi/ # Sami variants return 'smi' end end end DEFAULT_LANGUAGE end # Root endpoint - returns HTML by default get '/' do lang = determine_language(params, request.env) no_response = get_no_response(lang) case request.accept.first&.to_s when /application\/json/ content_type :json { answer: no_response, language: lang, language_name: get_language_name(lang), timestamp: Time.now.iso8601 }.to_json else content_type :html erb :index, locals: { no_response: no_response, language: lang, language_name: get_language_name(lang), available_languages: get_available_languages } end end # Languages endpoint - list available languages get '/languages' do content_type :json { languages: get_available_languages, default: DEFAULT_LANGUAGE, timestamp: Time.now.iso8601 }.to_json end # Explicit JSON endpoint with language support get '/api/no' do lang = determine_language(params, request.env) content_type :json { answer: get_no_response(lang), language: lang, language_name: get_language_name(lang), timestamp: Time.now.iso8601, service: "No as a Service", version: "2.0.0" }.to_json end # Language-specific endpoint get '/api/no/:lang' do lang = params[:lang] unless RESPONSES[lang] content_type :json status 404 return { error: "Language not supported", requested: lang, available: get_available_languages.map { |l| l[:code] }, timestamp: Time.now.iso8601 }.to_json end content_type :json { answer: get_no_response(lang), language: lang, language_name: get_language_name(lang), timestamp: Time.now.iso8601, service: "No as a Service", version: "2.0.0" }.to_json end # Health check endpoint get '/health' do content_type :json { status: "healthy", languages_loaded: RESPONSES.keys.size, timestamp: Time.now.iso8601 }.to_json end # Catch-all route for any other endpoint get '/*' do lang = determine_language(params, request.env) no_response = get_no_response(lang) case request.accept.first&.to_s when /application\/json/ content_type :json { answer: no_response, language: lang, language_name: get_language_name(lang), timestamp: Time.now.iso8601 }.to_json else content_type :html erb :index, locals: { no_response: no_response, language: lang, language_name: get_language_name(lang), available_languages: get_available_languages } end end __END__ @@index
The definitive multilingual API for negative responses
JSON API: GET /api/no or GET /api/no/:lang
Languages: GET /languages
URL Parameter: ?lang=<%= language %>
Accept-Language header supported
Health Check: GET /health